package common.asyncflowlog;

import com.alibaba.fastjson.JSON;
import common.annotation.NodeInfo;
import common.dateutils.AsyncFlowDateUtil;
import common.mongodb.MongoHelper;
import common.sendmsg.AsyncFlowSendMsg;
import constant.ExecuteEnum;
import models.*;
import org.apache.commons.lang.StringUtils;
import org.bson.Document;

import java.lang.annotation.Annotation;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@SuppressWarnings("unchecked")
public class AsyncFlowCommon_Log {


    /**
     * @param request               请求参数
     * @param asyncFlowNodeResponse 返回参数
     * @param asyncData             上下文
     * @param obj                   执行体
     * @param startTime             开始执行时间
     * @param executeEnum           日志类型 1：验证   2：查询  3：操作   4：保存    5：保存后同步    6：保存后异步
     * @param isAsync               方法运行方式 是否异步
     */
    //执行过程中添加操作节点执行日志
    public static <TAsyncContext> void AddAsyncFlowLog(AsyncFlowRequest request, AsyncFlowNodeResponse asyncFlowNodeResponse, AsyncFlowData<TAsyncContext> asyncData, Object obj, Date startTime, ExecuteEnum executeEnum, boolean isAsync) {
        if (asyncFlowNodeResponse.isIgnoreNodeTrackLog() || (asyncData.getAsyncFlowInfo().isIgnoreFlowLog() && asyncData.getAsyncFlowInfo().getEvn().equals("product")))//节点内部标记不记录当前节点的轨迹日志
        {
            return;
        }
        Class<?> clazz = obj.getClass();
        String logMethodName = null;
        Annotation[] logMethods = clazz.getAnnotations();
        for (Annotation annotation : logMethods) {
            //获取方法名
            if (annotation instanceof NodeInfo) {
                logMethodName = ((NodeInfo) annotation).name();
                break;
            }
        }
        AsyncFlowNodeLogEntity asyncFlowLog = new AsyncFlowNodeLogEntity();
        asyncFlowLog.setBusinessSerialId(request.getBusinessSerialId());
        asyncFlowLog.setAsyncFlowModuleEnum(request.getAsyncFlowModuleEnum().getMsg());
        asyncFlowLog.setAsyncFlowNodeEnum(request.getAsyncFlowNodeEnum().getMsg());
        asyncFlowLog.setLogMethodName(logMethodName);
        asyncFlowLog.setLogFileName(clazz.getSimpleName());
        asyncFlowLog.setLogNodePath(clazz.getName());
        asyncFlowLog.setLogMsg(asyncFlowNodeResponse.getMsg());
        //SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        asyncFlowLog.setLogTime(LocalDateTime.now().minusHours(8));
        // asyncFlowLog.setLogTimeStr(sdf.format(new Date()));
        //asyncFlowLog.setStatus(asyncFlowNodeResponse.isStatus());
        asyncFlowLog.setCode(asyncFlowNodeResponse.getCode());
        asyncFlowLog.setAsync(isAsync);
        Long time = new Date().getTime() - startTime.getTime();
        asyncFlowLog.setUseTimes(time);
        asyncFlowLog.setExecuteEnumCode(executeEnum.getCode());
        List<AsyncFlowNodeDetailLogEntity> asyncFlowNodeDetailLogEntityList =
                asyncData.getAsyncFlowNodeDetailLogEntityList().stream().filter(c -> c.getLogFileName().equals(asyncFlowLog.getLogFileName())).collect(Collectors.toList());
        asyncData.getAsyncFlowNodeDetailLogEntityList().removeAll(asyncFlowNodeDetailLogEntityList);

        if (asyncFlowNodeDetailLogEntityList.size() > 0) {
            asyncFlowLog.getAsyncFlowNodeDetailLogEntityList().addAll(0, asyncFlowNodeDetailLogEntityList);
        }
        asyncData.getAsyncFlowNodeLogEntityList().add(asyncFlowLog);
    }


    /**
     * @param asyncData     上下文
     * @param logFileName   文件名称
     * @param LogDetailName 日志名称
     * @param request       请求
     * @param response      返回
     * @param status        状态
     */
    public static void AddAsyncFlowDetailLog(AsyncFlowData asyncData, String logFileName, String LogDetailName, Object request, Object response, boolean status) {
        if (asyncData.getAsyncFlowInfo().isIgnoreFlowLog() && asyncData.getAsyncFlowInfo().getEvn().equals("product")) {
            return;
        }
        AsyncFlowNodeDetailLogEntity asyncFlowLogDetailEntity = new AsyncFlowNodeDetailLogEntity();
        //日志所在文件类名称
        asyncFlowLogDetailEntity.setLogFileName(logFileName);
        asyncFlowLogDetailEntity.setLogDetailName(LogDetailName);
        String reqLog = "";
        String resLog = "";
        if (request != null) {
            reqLog = JSON.toJSONString(request);
        }
        if (response != null) {
            resLog = JSON.toJSONString(response);
        }
        asyncFlowLogDetailEntity.setRequest(reqLog);
        asyncFlowLogDetailEntity.setResponse(resLog);
        asyncFlowLogDetailEntity.setStatus(status);
        asyncData.getAsyncFlowNodeDetailLogEntityList().add(asyncFlowLogDetailEntity);
    }


    /**
     * 执行完毕后保存节点执行日志
     *
     * @param request       请求参数
     * @param asyncFlowData 上下文
     * @param response      返回参数
     * @param timeConsuming 耗时详细
     */
    public static <TAsyncContext, TRes> void SaveExecuteTrackLogs(AsyncFlowRequest request, AsyncFlowData<TAsyncContext> asyncFlowData, AsyncFlowResponse<TRes> response, LinkedHashMap<ExecuteEnum, Long> timeConsuming, boolean allNodeIsSuccess, AsyncFlowInfo asyncFlowInfo) {

        if (asyncFlowData.getAsyncFlowInfo().isIgnoreFlowLog() && asyncFlowData.getAsyncFlowInfo().getEvn().equals("product")) {
            return;
        }
        boolean errorRequest = false;
        //总耗时和耗时明细组装
        String uptimeDes;
        long uptime = 0;
        StringBuilder stringBuilderUptimeDes = new StringBuilder();

        for (Map.Entry<ExecuteEnum, Long> entry : timeConsuming.entrySet()) {
            long time = entry.getValue();
            stringBuilderUptimeDes.append("【").append(entry.getKey().getMsg()).append("--").append(time).append("ms").append("】");
            uptime = uptime + time;
        }
        uptimeDes = "【总耗时--" + uptime + "ms】" + stringBuilderUptimeDes.toString();
        //保存流程运行轨迹
        AsyncFlowLogEntity asyncFlowLogEntity = new AsyncFlowLogEntity();
        try {
            if (StringUtils.isBlank(request.getBusinessSerialId())) {
                errorRequest = true;
                String dateGuid = AsyncFlowDateUtil.htFormatyyyyMMddHHmmV2(request.getRequesterTime());
                ;
                asyncFlowLogEntity.setBusinessSerialId(request.getAsyncFlowNodeEnum().getMsg() + "-" + dateGuid);
            } else {
                asyncFlowLogEntity.setBusinessSerialId(request.getBusinessSerialId());
            }

            DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            asyncFlowLogEntity.setLogTime(dtf2.format(request.getRequesterTime()));
            asyncFlowLogEntity.setAsyncFlowModule(request.getAsyncFlowModuleEnum().getMsg() + "-" + request.getAsyncFlowNodeEnum().getMsg());
            asyncFlowLogEntity.setTrackGuid(UUID.randomUUID().toString().toLowerCase().replaceAll("-", ""));
            asyncFlowLogEntity.setLogDateTime(LocalDateTime.now().minusHours(8));

            String resLog = "";
            if (response != null) {

                resLog = JSON.toJSONString(response);
            }
            asyncFlowLogEntity.setRequest(JSON.toJSONString(request));
            asyncFlowLogEntity.setResponse(resLog);
            asyncFlowLogEntity.setStatus(allNodeIsSuccess);
            asyncFlowLogEntity.setLogInfo(uptimeDes);
            asyncFlowLogEntity.setOperator(request.getOperateName() + "-" + request.getOperateNo());
            asyncFlowLogEntity.setSourceDes(request.getSourceDes());

            asyncFlowLogEntity.setUseTime(uptime);
            List<AsyncFlowNodeLogEntity> orderFlowLogs =
                    asyncFlowData.getAsyncFlowNodeLogEntityList().stream().sorted(Comparator.comparing(AsyncFlowNodeLogEntity::getLogTime)).collect(Collectors.toList());
            asyncFlowLogEntity.setAsyncFlowNodeLogEntityList(orderFlowLogs);

            String mongoDBConfig = asyncFlowInfo.getMongoDBConfigStr();

            //环境
            String evn = asyncFlowInfo.getEvn();
            asyncFlowLogEntity.setEnvironments(evn);
            MongoHelper<AsyncFlowLogEntity> mongoHelper;
            assert mongoDBConfig != null;
            if (mongoDBConfig.equals("close") || StringUtils.isEmpty(mongoDBConfig)) {
                return;
            } else {
                mongoHelper = new MongoHelper<>(mongoDBConfig, asyncFlowInfo.getMongoDBName(), asyncFlowInfo.getMongoDBTableName());
            }
            Document document = mongoHelper.toDocument(asyncFlowLogEntity);
            mongoHelper.insertOne(document);
            mongoHelper.closeMongoClient();
            //region 发送超时报警
            if (asyncFlowInfo.getTimeOutsLimit() <= 0) {
                asyncFlowInfo.setTimeOutsLimit(6000);
            }

            if (StringUtils.isNotBlank(asyncFlowInfo.getFlowMaintainName()) && uptime > asyncFlowInfo.getTimeOutsLimit()) {
                String serialid = request.getBusinessSerialId();

                if (StringUtils.isBlank(serialid)) {
                    String dateGuid = AsyncFlowDateUtil.htFormatyyyyMMddHHmmV2(request.getRequesterTime());
                    ;
                    serialid = request.getAsyncFlowNodeEnum().getMsg() + "-" + dateGuid;
                }
                String stringBuilder = "【节点超时报警】" +
                        "\n节点：" + request.getAsyncFlowModuleEnum().getMsg() + "--" + request.getAsyncFlowNodeEnum().getMsg() +
                        "\n来源：" + request.getSourceDes() +
                        "\n单号：" + serialid +
                        "\n时间：" + AsyncFlowDateUtil.htFormat(LocalDateTime.now()) +
                        "\n环境：" + asyncFlowInfo.getEvn() +
                        "\n耗时：" + uptime +
                        "\n明细：" + uptimeDes;
                AsyncFlowSendMsg.SendQYWXMsg(asyncFlowInfo.getFlowMaintainName(), stringBuilder, asyncFlowInfo.getAlarmUrl());
            }
            //endregion

            //异常请求，没有唯一标示
            if (errorRequest) {
                AsyncFlowSendMsg.sendAlarmMsg(request, "请求参数中唯一号BusinessSerialId为空", "SaveExecuteTrackLogs", asyncFlowData.getAsyncFlowInfo());
            }
        } catch (Exception e) {
            String msg = AsyncFlowSendMsg.buildErrorMsg(request, e, "SaveExecuteTrackLogs", asyncFlowInfo);
            AsyncFlowSendMsg.SendQYWXMsg(asyncFlowInfo.getFlowMaintainName(), msg, asyncFlowInfo.getAlarmUrl());
        }


    }


}
